import { RawAssetsUrl } from '../../raw-asset-info';
import { DefaultStage } from '../default-stage';
import { COMMUNITY_MODEL_PROPERTY_KEY, ICityData, ICommunityData, IFortressData, ITownData, IVillageData } from '../../data/community';
import { Helper } from 'babylon-lib/tool/helper';
import { Vector3 } from 'babylon-lib/index';
import { BASE_TOTAL_POPULATION, CITY_POPULATION_RATIO, FORTRESS_POPULATION_RATIO, TOWN_POPULATION_RATIO, VILLAGE_POPULATION_RATIO } from '../../data/data';
import { Random } from 'babylon-lib/tool/random';


class CommunityData {

    public static readonly cityData: ICityData[] = [];
    public static readonly fortressData: IFortressData[] = [];
    public static readonly townData: ITownData[] = [];
    public static readonly villageData: IVillageData[] = [];
    public static readonly allData: ICommunityData[] = [];

    private static _stage: DefaultStage;

    public static setup(stage: DefaultStage): void {
        this._stage = stage;
        this.extractData();
        this.allData.push(...this.cityData, ...this.fortressData, ...this.townData, ...this.villageData);
        this.asignPopulation();
        this.addPathData();
    }

    private static addPathData(): void {
        const loader = this._stage.loader(this._stage.mainScene);
        const allNodes = [
            ...loader.getMeshTaskInfo(RawAssetsUrl.modelChengweizhi)!.loadedTransformNodes!,
            ...loader.getMeshTaskInfo(RawAssetsUrl.modelZhenweizhi)!.loadedTransformNodes!,
            ...loader.getMeshTaskInfo(RawAssetsUrl.modelGuanweizhi)!.loadedTransformNodes!,
            ...loader.getMeshTaskInfo(RawAssetsUrl.modelCunweizhi)!.loadedTransformNodes!
        ];
        const pathExist = (target: ICommunityData, item: ICommunityData) => {
            for (const pd of target.pathTo) {
                if (pd.rawName === item.rawName) return true;
            }
            return false;
        };
        for (const data of this.allData) {
            const rawName = data.rawName;
            let dataNode = null;
            for (const node of allNodes) {
                if (node.name !== rawName) continue;
                dataNode = node;
                break;
            }
            if (!dataNode) {
                console.warn('Redundant data:', data);
                continue;
            }
            const lns = Helper.modelProperty<string | undefined>(dataNode, COMMUNITY_MODEL_PROPERTY_KEY.PATH);
            if (!lns) {
                console.warn('Data silos:', data);
                continue;
            }
            const splits = lns.split(/[^\p{L}\p{N}]+/u);
            for (const ln of splits) {
                const idx = this.allData.findIndex(d => d.rawName === ln);
                if (idx < 0) continue;
                if (!pathExist(data, this.allData[idx])) data.pathTo.push(this.allData[idx]);
                if (!pathExist(this.allData[idx], data)) this.allData[idx].pathTo.push(data);
            }
        }
    }

    private static asignPopulation(): void {
        const total = BASE_TOTAL_POPULATION;
        const weight = CommunityData.cityData.length * CITY_POPULATION_RATIO +
            CommunityData.townData.length * TOWN_POPULATION_RATIO +
            CommunityData.fortressData.length * FORTRESS_POPULATION_RATIO +
            CommunityData.villageData.length * VILLAGE_POPULATION_RATIO;
        const ppw = total / weight;
        const setRandowmPopulation = (baseRatio: number): number => {
            const rand = Random.float(0.8, 1.2);
            return Math.round(ppw * rand * baseRatio);
        };
        this.cityData.forEach(c => c.population = setRandowmPopulation(CITY_POPULATION_RATIO));
        this.townData.forEach(t => t.population = setRandowmPopulation(TOWN_POPULATION_RATIO));
        this.fortressData.forEach(f => f.population = setRandowmPopulation(FORTRESS_POPULATION_RATIO));
        this.villageData.forEach(v => v.population = setRandowmPopulation(VILLAGE_POPULATION_RATIO));
    }

    private static extractData(): void {
        const loader = this._stage.loader(this._stage.mainScene);
        const chineseName = (str: string): string => {
            return str.replace(/[^\u4e00-\u9fa5]/g, '');
        };
        const offset = new Vector3(0, -4, 0);

        const cityModel = loader.getMeshTaskInfo(RawAssetsUrl.modelChengweizhi)?.loadedTransformNodes!;
        for (const n of cityModel) {
            this.cityData.push({
                rawName: n.name,
                name: chineseName(n.name),
                location: new Vector3().copyFrom(n.position).addInPlace(offset),
                sovereign: Helper.modelProperty<string>(n, COMMUNITY_MODEL_PROPERTY_KEY.SOVEREIGN),
                pathTo: [],
            });
        }

        const fortressModel = loader.getMeshTaskInfo(RawAssetsUrl.modelGuanweizhi)?.loadedTransformNodes!;
        for (const n of fortressModel) {
            this.fortressData.push({
                rawName: n.name,
                name: chineseName(n.name),
                location: new Vector3().copyFrom(n.position).addInPlace(offset),
                sovereign: Helper.modelProperty(n, COMMUNITY_MODEL_PROPERTY_KEY.SOVEREIGN),
                pathTo: [],
            });
        }

        const townMdoel = loader.getMeshTaskInfo(RawAssetsUrl.modelZhenweizhi)?.loadedTransformNodes!;
        for (const n of townMdoel) {
            this.townData.push({
                rawName: n.name,
                name: chineseName(n.name),
                location: new Vector3().copyFrom(n.position).addInPlace(offset),
                sovereign: Helper.modelProperty(n, COMMUNITY_MODEL_PROPERTY_KEY.SOVEREIGN),
                pathTo: [],
            });
        }

        const villageModel = loader.getMeshTaskInfo(RawAssetsUrl.modelCunweizhi)?.loadedTransformNodes!;
        for (const n of villageModel) {
            this.villageData.push({
                rawName: n.name,
                name: chineseName(n.name),
                location: new Vector3().copyFrom(n.position).addInPlace(offset),
                pathTo: [],
            });
        }
    }

}


export { CommunityData };